package com.fengye.security.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fengye.common.annotation.CurrentUserId;
import com.fengye.common.constants.AppHttpCodeEnum;
import com.fengye.common.constants.CommonConstant;
import com.fengye.common.exception.BusinessException;
import com.fengye.common.utils.BeanCopyUtils;
import com.fengye.common.utils.JwtUtil;
import com.fengye.common.utils.RedisUtils;
import com.fengye.security.custom.model.LoginUserDetails;
import com.fengye.common.domain.entity.User;
import com.fengye.common.domain.vo.UserInfoVo;
import com.fengye.security.mapper.UserMapper;
import com.fengye.security.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.InternalAuthenticationServiceException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.DigestUtils;
import org.springframework.validation.annotation.Validated;

import jakarta.annotation.Resource;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

/**
* @author sunlei
* @description 针对表【fy_user】的数据库操作Service实现
* @createDate 2024-02-25 21:02:38
*/
@Slf4j
@Transactional(rollbackFor = {Exception.class, Error.class})
@Service // @Service注解：将当前类对象交给Spring来管理，自动创建对象以及对象的维护
public class UserServiceImpl extends ServiceImpl<UserMapper, User>
    implements UserService {

    @Resource
    private UserMapper userMapper;

    @Resource
    private RedisUtils redisUtils;

    @Resource
    private AuthenticationManager authenticationManager;

    @Resource
    private PasswordEncoder passwordEncoder;

    @Override
    public void reg(@Validated User user) {
        // 查询该用户是否被注册过
        User result = userMapper.findByUsername(user.getUsername());
        if (result != null) {
            throw  new BusinessException(AppHttpCodeEnum.USER_ALREADY_EXIST);
        }

        // 加密数据
        String newPassword = passwordEncoder.encode(user.getPassword());
        // 设置加密后的密码
        user.setPassword(newPassword);

        // 执行插入操作
        int rows = userMapper.insert(user);
        if (rows != 1) {
            throw new BusinessException(AppHttpCodeEnum.USER_REG_ERROR);
        }
    }

    @Override
    public Map<String, Object> login(String username, String password) {

        String token = null;
        UserInfoVo userInfoVo = null;
        Authentication authenticate = null;
        try {
            // 调用authenticationManager的authenticate方法去认证
            UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(username, password);
            authenticate = authenticationManager.authenticate(usernamePasswordAuthenticationToken);

            // 如果不为空，则认证成功
            if (!Objects.isNull(authenticate)) {
                // 如果认证成功，将认证信息存储在SecurityContext中
                SecurityContextHolder.getContext().setAuthentication(authenticate);
            }
        } catch (AuthenticationException e) {
            AppHttpCodeEnum appHttpCodeEnum = AppHttpCodeEnum.USERNAME_OR_PWD_ERROR;
            if (e instanceof InternalAuthenticationServiceException) {
                appHttpCodeEnum= AppHttpCodeEnum.USER_NOT_FOUND;
            } else if (e instanceof BadCredentialsException) {
                appHttpCodeEnum = AppHttpCodeEnum.USER_PWD_ERROR;
            }
            throw new BusinessException(appHttpCodeEnum);
        }

        // 认证成功，使用userid生成一个jwt
        LoginUserDetails loginUser = (LoginUserDetails)authenticate.getPrincipal();

        String userId = loginUser.getUser().getId().toString();
        token = JwtUtil.genAccessToken(userId);

        // 把完整的用户信息存入redis，有效期7天
        redisUtils.setEx(CommonConstant.REDIS_LOGIN_USER_DETAILS_KEY_PREFIX + userId, JSON.toJSONString(loginUser), JwtUtil.ACCESS_EXPIRE);
        // token 存起来
        redisUtils.setEx(CommonConstant.REDIS_LOGIN_TOKEN_KEY_PREFIX + userId, token, JwtUtil.ACCESS_EXPIRE);

        // 获取用户信息
        User user = userMapper.findByUid(loginUser.getUser().getId());
        if (user == null || "1".equals(user.getDelFlag())) {
            throw new BusinessException(AppHttpCodeEnum.USER_NOT_FOUND);
        }
        userInfoVo = BeanCopyUtils.copyBean(user, UserInfoVo.class);

        // 构造接口数据
        HashMap<String, Object> hashMap = new HashMap<>();
        hashMap.put("token", token);
        hashMap.put("userInfo", userInfoVo);

        return hashMap;
    }

    @Override
    public void logout(@CurrentUserId Long userId) {
        // 删除redis中的值
        redisUtils.delete(CommonConstant.REDIS_LOGIN_USER_DETAILS_KEY_PREFIX + userId);
        // 清楚Security中SecurityContext中的认证的信息
        SecurityContextHolder.clearContext();
    }

    @Override
    public void changePassword(Long userId, String oldPassword, String newPassword) {
        User user = userMapper.findByUid(userId);
        if (user == null || "1".equals(user.getDelFlag())) {
            throw new BusinessException(AppHttpCodeEnum.USER_NOT_FOUND);
        }

        String encode = passwordEncoder.encode(oldPassword);
        System.out.println(encode);

        if (!passwordEncoder.matches(oldPassword, user.getPassword())) {
            throw new BusinessException(AppHttpCodeEnum.USERNAME_OR_PWD_ERROR);
        }

        String newEncryptPassword = passwordEncoder.encode(newPassword);
        Integer rows = userMapper.updatePasswordByUid(userId, newEncryptPassword, user.getUsername(), new Date());
        if (rows != 1) {
            throw new BusinessException(AppHttpCodeEnum.MODIFY_PWD_ERROR);
        }
    }

    @Override
    public User getUserinfo(Long userId) {
        User user = userMapper.findByUid(userId);
        if (user == null || "1".equals(user.getDelFlag())) {
            throw new BusinessException(AppHttpCodeEnum.USER_NOT_FOUND);
        }
        return user;
    }

    @Override
    public void changeUserinfo(User user) {

        User result = userMapper.findByUid(user.getId());
        if (result == null) {
            throw new BusinessException(AppHttpCodeEnum.USER_NOT_FOUND);
        }

        Integer rows = userMapper.updateUserinfoByUid(user);
        if (rows != 1) {
            throw new BusinessException(AppHttpCodeEnum.MODIFY_USERINFO_ERROR);
        }
    }

    @Override
    public void changeAvatar(Long uid, String avatar) {
        User result = userMapper.findByUid(uid);

        if (result == null || "1".equals(result.getDelFlag())) {
            throw new BusinessException(AppHttpCodeEnum.USER_NOT_FOUND);
        }

        Integer rows = userMapper.updateAvatar(uid, avatar, result.getUsername(), new Date());
        if (rows != 1) {
            throw new BusinessException(AppHttpCodeEnum.MODIFIED_AVATAR_ERROR);
        }
    }

    /**
     * 获取加密后的密码
     * @param password 明文密码
     * @param salt 盐
     * @return 加密后的密码
     */
    private  String getMd5Password(String password, String salt) {
        // 加密三次，加强安全
        for (int i = 0; i < 3; i++) {
            password = DigestUtils.md5DigestAsHex(((salt + password + salt).getBytes())).toUpperCase();
        }

        return password;
    }
}




